/*
 * Lanzador.cpp
 *
 *  Created on: 25/02/2010
 *      Author: San Martín, Matías I.
 */

#include "Lanzador.h"

using namespace framework::io;

namespace aplicacion {
namespace planeta {

//----------------- inicializacion de atributos (static) -----------------------
Semaphore 			Lanzador::_semAccesoInfoPipe;
Semaphore 			Lanzador::_semLeyendoDatos;
t_sharedRegistro 	Lanzador::_datosRegitro;
InformacionPlaneta 	Lanzador::_infoPlanetaAlt;

pid_t 				Lanzador::_pidMsjReceptor = -1;
pid_t 				Lanzador::_pidPlaneta = -1;

//----------------- definicion de metodos ----------------------------

void Lanzador::inicializarRecursos()
{
	//inicializo el logger
	logger.set_pname("Lanzador en PLANETA");

	//creo recursos compartidos
	if (!_semAccesoInfoPipe.init(1))
	{
		logger << "Fallo inicializacion semAccesoInfoPipe. " << logger.endl;
		exit(-1);
	}

	if (!_semLeyendoDatos.init(1))
	{
		logger << "Fallo inicializacion semLeyendoDatos. " << logger.endl;
		exit(-1);
	}

	if (!_datosRegitro.allocate())
	{
		logger << "Fallo creacion de datosRegistro. " << logger.endl;
		exit(-1);
	}

	aplicacion::RegistrySingleton::instance().setDirectorio(
			&aplicacion::DirectorioSingleton::instance());

	logger << "Inicializados los recursos" << logger.endl;
}

void Lanzador::liberarRecursos()
{
	_semAccesoInfoPipe.destroy();
	_semLeyendoDatos.destroy();
	_datosRegitro.free();

	//TODO LIBERAR REGISTRO

	logger << "Liberados los recursos" << logger.endl;
}

void Lanzador::abortar (const std::string& errorMsj)
{
	logger << "Abortando: " << errorMsj << logger.endl;
	exit(-1);
}

pid_t Lanzador::lanzarMsjReceptor()
{
	pid_t ret = fork();

	//en el proceso hijo se ejecuta el receptor de mensajes
	if (ret == 0)
	{
		try
		{
			//ccreo e inicializo el proceso receptor que corre en el middleware
			MsjReceptorMidSingleton::instance().inicializar
					(&MiddlewareClienteSingleton::instance(),&_semLeyendoDatos,&_datosRegitro);
			MsjReceptorMidSingleton::instance().start();

			//se termina la ejecucion correctamente
			exit(0);
		}catch (...)
		{
			abortar("Excepcion en el inicio de MsjReceptor");
		}
	}

	//TODO VERIFICAR QUE EL MIDDLEWARE SE INICIO CORRECTAMENTE
	return ret;
}

void Lanzador::iniciarMiddleware()
{
	if (!MiddlewareClienteSingleton::instance().start())
	{
		logger << "Error al inicializar middleware IO" << logger.endl;
		exit (-1);
	}

	_pidMsjReceptor = lanzarMsjReceptor();
}

pid_t Lanzador::iniciarPlaneta(const InformacionPlaneta& info)
{
	pid_t ret = fork();
	if (ret == 0)
	{
		//creo el proceso receptor que corre "en la aplicacion"
		MsjReceptorApp recept;
		recept.inicializar(&_semAccesoInfoPipe,&_semLeyendoDatos,&_datosRegitro,_pidMsjReceptor);

		MsjEmisor emisor;
		emisor.inicializar(&MiddlewareClienteSingleton::instance());

		//---------------------------- creacion del planeta ------------------

		DatosMensajeFactorySingleton::instance().setContexto(DatosMensajeFactory::CONTEXTO_PLANETA);

		//creo el planeta con la informacion dada
		Planeta p(info._identificador,info._tamanio,info._posicion);
		p.init(&recept,&emisor);

		//inicio ejecucion del planeta
		p.run();

		//termino la ejecucion correctamente
		exit (0);
	}

	return ret;
}

InformacionCreacionUniverso Lanzador::elegirUniverso()
{
	t_listaUniversos universos = RegistrySingleton::instance()
	.getDirectorio().listarUniversosAbiertos();

	if (universos.size() == 0)
	{
		std::cerr << "\nNo hay universos para elegir\n";
		InformacionCreacionUniverso ret;
		ret._idUniverso = -1;
		return ret;
	}else{

		//muestro los universos disponibles
		size_t indice = 0;
		t_listaUniversos::iterator fin = universos.end();
		for (t_listaUniversos::iterator i = universos.begin(); i != fin; i++)
		{
			indice++;
			std::cout << indice << "- Universo " << *i << "\n";
		}

		//se elije el universo deseado
		size_t elegido = 0;
		do {
			std::cout << "\nElija el universo al cual quiere agregar el planeta: ";
			std::cin >> elegido;
		}while (elegido == 0 || elegido > indice /* cantidad de universos */);

		/* obtengo el iterador a la posicion elegida */
		t_listaUniversos::iterator i;
		for ( i = universos.begin(); --elegido; i++){}

		//guardo el universo elegido
		InformacionCreacionUniverso uelegido = *i;

		return uelegido;
	}

}

char Lanzador::elegirAccion()
{
	char opt = 0;
	do
	{
		std::cout << "Elija opcion: "
				"\n1- Crear planeta asociado a universo"
				"\n2- Crear universo"
				"\n0- Terminar" << std::endl;
		std::cin >> opt;
	} while (opt != OPT_NUEVO_PLANETA
			&& opt != OPT_TERMINAR
			&& opt != OPT_NUEVO_UNIVERSO);

	return opt;
}

void Lanzador::logearInfoPlaneta(const InformacionPlaneta& info)
{
	logger << "Planeta: " << info._identificador;
	logger << "\nTamaño del planeta: " << info._tamanio;
	logger << "\nUbicacion del planeta: " << info._posicion << logger.endl;
}

InformacionPlaneta Lanzador::definirPlaneta()
{
	InformacionPlaneta info;

	std::cout << "Tamaño del planeta (numero positivo): ";
	std::cin >> info._tamanio;

	t_coordenada x,y;
	std::cout << "Coordenada X de la posicion del planeta (numero positivo): ";
	std::cin >> x;

	std::cout << "Coordenada Y de la posicion del planeta (numero positivo): ";
	std::cin >> y;
	info._posicion.setXY(x,y);

	return info;
}

InformacionCreacionUniverso Lanzador::definirUniverso()
{
	InformacionCreacionUniverso datos;
	std::cout << "Cantidad maxima de planetas: ";
	std::cin >> datos._maxPlanetas;

	std::cout << "Cantidad minima de planetas: ";
	std::cin >> datos._minPlanetas;

	std::cout << "Timeout de creacion: ";
	std::cin >> datos._timeout;
	return datos;
}

void  Lanzador::crearPlaneta(const InformacionCreacionUniverso& u)
{
	//se elije las caracteristicas del planeta
	InformacionPlaneta p = definirPlaneta();

	//se registra el planeta
	//si no se pudo registrar
	if (!RegistrySingleton::instance().getDirectorio().add(u,p))
	{
		logger << "ERROR: no se pudo registrar el planeta al"
				" universo con id " << u._idUniverso << logger.endl;
	}else{
		logearInfoPlaneta(p);
		logger << "Registrado en el universo con id " << u._idUniverso << logger.endl;

		//se inicia la instancia que representara el planeta registrado
		//TODO GUARDAR EL PID?
		_pidPlaneta = iniciarPlaneta(p);
	}
}
void Lanzador::crearUniverso()
{
	InformacionCreacionUniverso info = definirUniverso();

	//se registra el planeta
	//si no se pudo registrar
	if (!RegistrySingleton::instance().getDirectorio().registrarNuevo(info))
	{
		logger << "ERROR: no se pudo registrar el nuevo universo" << logger.endl;
	}else{
		logger << "Registrado universo correctamente ";
		logger << "\n\tUniverso: " << info._idUniverso;
		logger << "\n\tCantidad maxima de planetas: " << info._maxPlanetas;
		logger << "\n\tCantidad minima de planetas: " << info._minPlanetas;
		logger << "\n\tTimeout de creacion: " << info._timeout << logger.endl;
	}
}

void Lanzador::terminar() {
	// jgrande
	// es importante terminar primero el planeta, porque si se hace al revés,
	// el planeta sale del select y cree que le llegó un mensaje
	if(_pidPlaneta!=-1) {
		logger << LogType::LOG_DEBUG_1 << "interrumpiendo Planeta" << logger.endl;
		if(kill(_pidPlaneta, SIGINT)!=-1) {
			while(waitpid(_pidPlaneta, NULL, 0)!=_pidPlaneta);
		} else {
			logger << LogType::LOG_ERROR << "Error interrumpiendo Planeta"
					<< logger.err << logger.endl;
		}
	}

	if(_pidMsjReceptor!=-1) {
		logger << LogType::LOG_DEBUG_1 << "interrumpiendo MsjReceptor" << logger.endl;
		if(kill(_pidMsjReceptor, SIGINT)!=-1) {
			while(waitpid(_pidMsjReceptor, NULL, 0)!=_pidMsjReceptor);
		} else {
			logger << LogType::LOG_ERROR << "Error interrumpiendo MsjReceptor"
					<< logger.err << logger.endl;
		}
	}

	MiddlewareClienteSingleton::instance().finish();

	liberarRecursos();

	logger << LogType::LOG_INFO << "Planeta detenido";
}

void Lanzador::run()
{
	char opt;

	inicializarRecursos();
	iniciarMiddleware();

	while ((opt = elegirAccion()) != OPT_TERMINAR) {


		switch (opt) {
			case OPT_NUEVO_PLANETA:
			{
				//se elije el universo
				InformacionCreacionUniverso u = elegirUniverso();
				if (u._idUniverso > 0)
				{
					crearPlaneta(u);
				}
				break;
			}
			case OPT_NUEVO_UNIVERSO:
			{
				crearUniverso();
				break;
			}
		}

		std::cout <<
				"\n\n\n\n---------------------------------------------------------\n\n\n"
				<< std::endl;
	}

	terminar();
}


} //namespace planeta
} //namespace aplicacion

int main(int argc, char* argv[]) {
	logger.set_pname("Planeta");
	//Se define el nivel de logueo deseado
	LogConfiguracionSingleton::instance().nivelLogueo = LogType::LOG_INFO;
	aplicacion::planeta::Lanzador lanzador;
	lanzador.run();
}
